// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ogle

import (
	"debug/proc";
	"exp/eval";
	"reflect";
)

// This file contains remote runtime definitions.  Using reflection,
// we convert all of these to interpreter types and layout their
// remote representations using the architecture rules.
//
// We could get most of these definitions from our own runtime
// package; however, some of them differ in convenient ways, some of
// them are not defined or exported by the runtime, and having our own
// definitions makes it easy to support multiple remote runtime
// versions.  This may turn out to be overkill.
//
// All of these structures are prefixed with rt1 to indicate the
// runtime version and to mark them as types used only as templates
// for remote types.

/*
 * Runtime data headers
 *
 * See $GOROOT/src/pkg/runtime/runtime.h
 */

type rt1String struct {
	str	uintptr;
	len	int;
}

type rt1Slice struct {
	array	uintptr;
	len	int;
	cap	int;
}

type rt1Eface struct {
	typ	uintptr;
	ptr	uintptr;
}

/*
 * Runtime type structures
 *
 * See $GOROOT/src/pkg/runtime/type.h and $GOROOT/src/pkg/runtime/type.go
 */

type rt1UncommonType struct {
	name	*string;
	pkgPath	*string;
	//methods []method;
}

type rt1CommonType struct {
	size			uintptr;
	hash			uint32;
	alg, align, fieldAlign	uint8;
	string			*string;
	uncommonType		*rt1UncommonType;
}

type rt1Type struct {
	// While Type is technically an Eface, treating the
	// discriminator as an opaque pointer and taking advantage of
	// the commonType prologue on all Type's makes type parsing
	// much simpler.
	typ	uintptr;
	ptr	*rt1CommonType;
}

type rt1StructField struct {
	name	*string;
	pkgPath	*string;
	typ	*rt1Type;
	tag	*string;
	offset	uintptr;
}

type rt1StructType struct {
	rt1CommonType;
	fields	[]rt1StructField;
}

type rt1PtrType struct {
	rt1CommonType;
	elem	*rt1Type;
}

type rt1SliceType struct {
	rt1CommonType;
	elem	*rt1Type;
}

type rt1ArrayType struct {
	rt1CommonType;
	elem	*rt1Type;
	len	uintptr;
}

/*
 * Runtime scheduler structures
 *
 * See $GOROOT/src/pkg/runtime/runtime.h
 */

// Fields beginning with _ are only for padding

type rt1Stktop struct {
	stackguard	uintptr;
	stackbase	*rt1Stktop;
	gobuf		rt1Gobuf;
	_args		uint32;
	_fp		uintptr;
}

type rt1Gobuf struct {
	sp	uintptr;
	pc	uintptr;
	g	*rt1G;
	r0	uintptr;
}

type rt1G struct {
	_stackguard	uintptr;
	stackbase	*rt1Stktop;
	_defer		uintptr;
	sched		rt1Gobuf;
	_stack0		uintptr;
	_entry		uintptr;
	alllink		*rt1G;
	_param		uintptr;
	status		int16;
	// Incomplete
}

var rt1GStatus = runtimeGStatus{
	Gidle: 0,
	Grunnable: 1,
	Grunning: 2,
	Gsyscall: 3,
	Gwaiting: 4,
	Gmoribund: 5,
	Gdead: 6,
}

// runtimeIndexes stores the indexes of fields in the runtime
// structures.  It is filled in using reflection, so the name of the
// fields must match the names of the remoteType's in runtimeValues
// exactly and the names of the index fields must be the capitalized
// version of the names of the fields in the runtime structures above.
type runtimeIndexes struct {
	String	struct {
		Str, Len int;
	};
	Slice	struct {
		Array, Len, Cap int;
	};
	Eface	struct {
		Typ, Ptr int;
	};

	UncommonType	struct {
		Name, PkgPath int;
	};
	CommonType	struct {
		Size, Hash, Alg, Align, FieldAlign, String, UncommonType int;
	};
	Type	struct {
		Typ, Ptr int;
	};
	StructField	struct {
		Name, PkgPath, Typ, Tag, Offset int;
	};
	StructType	struct {
		Fields int;
	};
	PtrType	struct {
		Elem int;
	};
	SliceType	struct {
		Elem int;
	};
	ArrayType	struct {
		Elem, Len int;
	};

	Stktop	struct {
		Stackguard, Stackbase, Gobuf int;
	};
	Gobuf	struct {
		Sp, Pc, G int;
	};
	G	struct {
		Stackbase, Sched, Status, Alllink int;
	};
}

// Values of G status codes
type runtimeGStatus struct {
	Gidle, Grunnable, Grunning, Gsyscall, Gwaiting, Gmoribund, Gdead int64;
}

// runtimeValues stores the types and values that correspond to those
// in the remote runtime package.
type runtimeValues struct {
	// Runtime data headers
	String, Slice, Eface	*remoteType;
	// Runtime type structures
	Type, CommonType, UncommonType, StructField, StructType, PtrType,
		ArrayType, SliceType	*remoteType;
	// Runtime scheduler structures
	Stktop, Gobuf, G	*remoteType;
	// Addresses of *runtime.XType types.  These are the
	// discriminators on the runtime.Type interface.  We use local
	// reflection to fill these in from the remote symbol table,
	// so the names must match the runtime names.
	PBoolType,
		PUint8Type, PUint16Type, PUint32Type, PUint64Type, PUintType, PUintptrType,
		PInt8Type, PInt16Type, PInt32Type, PInt64Type, PIntType,
		PFloat32Type, PFloat64Type, PFloatType,
		PArrayType, PStringType, PStructType, PPtrType, PFuncType,
		PInterfaceType, PSliceType, PMapType, PChanType,
		PDotDotDotType, PUnsafePointerType	proc.Word;
	// G status values
	runtimeGStatus;
}

// fillRuntimeIndexes fills a runtimeIndexes structure will the field
// indexes gathered from the remoteTypes recorded in a runtimeValues
// structure.
func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) {
	outv := reflect.Indirect(reflect.NewValue(out)).(*reflect.StructValue);
	outt := outv.Type().(*reflect.StructType);
	runtimev := reflect.Indirect(reflect.NewValue(runtime)).(*reflect.StructValue);

	// out contains fields corresponding to each runtime type
	for i := 0; i < outt.NumField(); i++ {
		// Find the interpreter type for this runtime type
		name := outt.Field(i).Name;
		et := runtimev.FieldByName(name).Interface().(*remoteType).Type.(*eval.StructType);

		// Get the field indexes of the interpreter struct type
		indexes := make(map[string]int, len(et.Elems));
		for j, f := range et.Elems {
			if f.Anonymous {
				continue
			}
			name := f.Name;
			if name[0] >= 'a' && name[0] <= 'z' {
				name = string(name[0]+'A'-'a') + name[1:len(name)]
			}
			indexes[name] = j;
		}

		// Fill this field of out
		outStructv := outv.Field(i).(*reflect.StructValue);
		outStructt := outStructv.Type().(*reflect.StructType);
		for j := 0; j < outStructt.NumField(); j++ {
			f := outStructv.Field(j).(*reflect.IntValue);
			name := outStructt.Field(j).Name;
			f.Set(indexes[name]);
		}
	}
}
